/**
 * 地磅串口转WebScoket
 * by 货当当
 * 版本V0.4
 * 版本日期 2021年09月24日
 * 
 * v0.4版本说明（2021年09月24日）
 *     + 把主文件拆为模块
 *     + 增加瑧识R3相机对应的开闸操作
 *  
 * v0.3版本说明（2021年09月23日）: 
 *     + 针对多磅多摄像头做了适配
 *     + 摄像头识别接口可接收客户端响应并放回当前所有车牌信息
 *     + 针对部分摄像头content-type不规范做了做了兼容
 *     + 优化numStack全局变量为局部变量
 */
const path         = require('path');
const https = require('https');
const fs = require('fs');
const express = require('express');
const cors = require('cors');
const app = express();
const SerialPort = require('serialport');
const schedule = require('node-schedule');
const child_process = require('child_process');

const {SerIP,SerDev,videoDev} = require('./config.js');// 引入配置
const {parser} = require('./parser.js'); // 引入地磅解析函数
const {barrier} = require('./barrier.js');

// ssl注入app
const httpsServ = https.createServer({
    key: fs.readFileSync('./'+SerIP+'cert.key','utf8'),
    cert: fs.readFileSync('./'+SerIP+'cert.pem','utf8'),
}, app);

// websocket注入app
const expressWs = require('express-ws')(app,httpsServ);

// app 跨域
app.use(cors());
//货主PC本地部署路径
app.use(express.static('shipper-dist/dist')); 
// 解析POST数据
app.use(express.json({type:["text/plain","*/json"]})); // type:plain参数针对部分content-type不规范的摄像头，如文通。
app.use(express.urlencoded({extended: false}));

/***ssl及websocket注入，下面可以写业务逻辑了***/

// 出错时统一回调函数
function errback(err) {
    if (err) {
      return console.log('Error: ', err.message)
    }
}

//git仓库同步，每5分钟更新
function scheduleCron(){
    if (!fs.existsSync('./shipper-dist')) {
        child_process.exec('d: && cd d:/dibang && git clone https://gitee.com/http_gn_guo_com/shipper-dist.git');
    }
    let rule = new schedule.RecurrenceRule();
    rule.minute = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55];
    schedule.scheduleJob(rule, function(){ 
        child_process.exec('d: && cd d:/dibang/shipper-dist && git pull', function (err, stdout, stderr) {       
            fs.writeFile('./git.log', stdout, function (err) {
                if (err) console.log(err);
                console.log('git pull: success');
            });
        });   
    });
}
scheduleCron();

// 获取磅秤的串口转ws
const getPound = function(devNum) {
    console.log('ok');
    let port = new SerialPort(SerDev[devNum]['dev'], SerDev[devNum]['options'],errback);
    let stack = []; // 此处把串口数据读到缓冲数组里。因为串口的特点，如果N个函数一起调用port.read会出错。
    port.on('readable', function () {
        let buff = parser.tf0( port.read() );
        if(buff!==false) stack.push(buff);
        
        if(stack.length>10) {
            stack.shift();
        }
        
     });

     // v0.2新增功能，用于监控 重连地磅
     port.on('close',function(){
        console.log('close');
        stack.length = 0;
     });

    SerDev[devNum]['restartHandler'] = function(){
        port.close(()=>{ 
            console.log('reconn');
            port.open(errback);
        });
    }

    return function(ws,req) {
        let interval
        // 连接成功后使用定时器定时向客户端发送数据，同时要注意定时器执行的时机，要在连接开启状态下才可以发送数据
        interval = setInterval(() => {
            if (ws.readyState === ws.OPEN) {
                let w = {stable:false,weight:"0.00"};
                if(stack.length>0) {
                    w.weight = stack[stack.length-1]; // 不要用stack.pop()，防止多台电脑连一个磅时数据不一致。
                }
                if(stack.length>=3 
                    &&  stack[stack.length-1]==stack[stack.length-2]
                    && stack[stack.length-1]==stack[stack.length-3])
                {
                    w.stable = true;
                }

                ws.send(JSON.stringify(w));

            } else {
                clearInterval(interval)
            }
        }, 100)
    };
}


// 车牌识别函数 // v0.3 针对多磅多摄像头区分
const getVehicle = function() {
    let numStack = {}; 
    
    let postHandler = function(req,res) {
        let ipaddr = req.body.AlarmInfoPlate.ipaddr;
        let license = req.body.AlarmInfoPlate.result.PlateResult.license;
 
        if( (license.length == 6) || (license.length==7) ) {
            numStack[ipaddr] = { vehicleNo:license , camerIndex:videoDev[ipaddr] };
        }
        //console.log(numStack);
        res.send('ok');
    };

    let wsHandler = function(ws,req) {
        let interval;
        // 连接成功后使用定时器定时向客户端发送数据，同时要注意定时器执行的时机，要在连接开启状态下才可以发送数据
        interval = setInterval(() => {
            if (ws.readyState === ws.OPEN) {
                for(let ip in numStack) {       
                    if(!numStack[ip].hasOwnProperty('expire')) {     
                        //console.log(numStack[ip]);
                        numStack[ip].expire = true;       
                        ws.send( JSON.stringify(numStack[ip]) );  
                    }
                }
            } else {
                clearInterval(interval)
            }
        }, 1000);

        ws.on('message', function(msg) { // 客户端请求，则把所有车牌号返回
            if(msg=='HeartBeat') return;
            for(let ip in numStack) {                  
                ws.send(JSON.stringify(numStack[ip]) );
            }
        });
    };

    return[postHandler , wsHandler];
}

const getVehicleInstance = getVehicle();

// v0.2重连磅头
app.get('/',function(req,res){
    res.send("hello dibang");
});

// 重连地磅
app.post('/weight/restartWeight',function(req,res){
    let key = req.body.poundNum;
    if( SerDev[key]!==undefined && SerDev[key]['restartHandler']!==null ) {
        SerDev[key]['restartHandler']();
    }
    res.send("success");
});

// 开启闸机
app.post('/openBarrier',function(req,res){
    let poundNum = req.body.poundNum;
    let poundIp = 0;
    for(let ip in videoDev) {
        if(videoDev[ip] == poundNum) {
            poundIp = ip;
            break;
        }
    }

    if(poundIp === 0) {
        return res.send('fail');
    }

    barrier(poundIp).then(()=>{
        res.send('success');
    }).catch(()=>{
        res.send('fail');
    });

});

// 读取地磅数据的websocket接口
app.ws('/firstWeightWebsocket', getPound(1));
app.ws('/secondWeightWebsocket', getPound(2));
//app.ws('/thirdWeightWebsocket', getPound(3));
//app.ws('/fourthWeightWebsocket', getPound(4));

app.post('/devicemanagement' , getVehicleInstance[0] ); // 接收摄像头推送的车牌号
app.ws('/vehicleRecoWebsocket' , getVehicleInstance[1] ); // 推送车牌号


httpsServ.listen(443,'0.0.0.0');